package com.waters321.rock.orm.config.datasource;

import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import com.baomidou.mybatisplus.spring.boot.starter.ConfigurationCustomizer;
import com.baomidou.mybatisplus.spring.boot.starter.MybatisPlusProperties;
import com.baomidou.mybatisplus.spring.boot.starter.SpringBootVFS;

@Configuration
@MapperScan(basePackages = {"com.waters321.rock.orm.dao.*.frame"},
    sqlSessionTemplateRef = "sqlSessionTemplateFrame")
public class FrameDataSourceConfig {

  @ConfigurationProperties(prefix = "spring.datasource.frame")
  @Bean(name = "datasourceFrame")
  @Primary
  public DruidDataSource datasource() throws SQLException {
    return DruidDataSourceBuilder.create().build();
  }

  @ConfigurationProperties(prefix = "spring.datasource.frame.mybatis_plus")
  @Bean(name = "mybatisPlusPropertiesFrame")
  @Primary
  public MybatisPlusProperties mybatisPlusProperties() {
    return new MybatisPlusProperties();
  }

  @Bean(name = "sqlSessionFactoryFrame")
  @Primary
  public SqlSessionFactory sessionFactory(
      @Qualifier(value = "datasourceFrame") DataSource dataSource,
      @Qualifier(value = "mybatisPlusPropertiesFrame") MybatisPlusProperties properties,
      @Autowired ResourceLoader resourceLoader,
      @Autowired ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
      @Autowired ObjectProvider<Interceptor[]> interceptorsProvider,
      @Autowired ObjectProvider<DatabaseIdProvider> databaseIdProvider) throws Exception {

    List<ConfigurationCustomizer> configurationCustomizers =
        configurationCustomizersProvider.getIfAvailable();
    Interceptor[] interceptors = interceptorsProvider.getIfAvailable();
    DatabaseIdProvider databaseIdProviderO = databaseIdProvider.getIfAvailable();

    MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(properties.getConfigLocation())) {
      factory.setConfigLocation(resourceLoader.getResource(properties.getConfigLocation()));
    }
    MybatisConfiguration configuration = properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(properties.getConfigLocation())) {
      configuration = new MybatisConfiguration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
    factory.setConfiguration(configuration);
    if (properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(interceptors)) {
      factory.setPlugins(interceptors);
    }
    if (databaseIdProviderO != null) {
      factory.setDatabaseIdProvider(databaseIdProviderO);
    }
    if (StringUtils.hasLength(properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(properties.getTypeEnumsPackage())) {
      factory.setTypeEnumsPackage(properties.getTypeEnumsPackage());
    }
    if (StringUtils.hasLength(properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(properties.resolveMapperLocations())) {
      factory.setMapperLocations(properties.resolveMapperLocations());
    }
    if (!ObjectUtils.isEmpty(properties.getGlobalConfig())) {
      factory.setGlobalConfig(properties.getGlobalConfig().convertGlobalConfiguration());
    }
    return factory.getObject();
  }

  @Bean(name = "transactionManagerFrame")
  @Primary
  public DataSourceTransactionManager transactionManager(
      @Qualifier("datasourceFrame") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }

  @Bean(name = "sqlSessionTemplateFrame")
  @Primary
  public SqlSessionTemplate sqlSessionTemplate(
      @Qualifier(value = "mybatisPlusPropertiesFrame") MybatisPlusProperties properties,
      @Qualifier("sqlSessionFactoryFrame") SqlSessionFactory sqlSessionFactory) throws Exception {
    ExecutorType executorType = properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }

}
